/***************************************************************************

Copyright (c) Microsoft Corporation. All rights reserved.
This code is licensed under the Visual Studio SDK license terms.
THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.

***************************************************************************/

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.TextManager.Interop;
using Microsoft.VisualStudio.Package;
using Wicresoft.VelocityLanguage.LanguageService;
using Babel.Parser;
using UmcControls.DesignInterface;
using System.Reflection;

namespace Babel
{
	public abstract class BabelLanguageService : Microsoft.VisualStudio.Package.LanguageService
	{
		#region Custom Colors
		public override int GetColorableItem(int index, out IVsColorableItem item)
		{
			if (index <= Configuration.ColorableItems.Count)
			{
				item = Configuration.ColorableItems[index - 1];
				return Microsoft.VisualStudio.VSConstants.S_OK;
			}
			else
			{
				throw new ArgumentNullException("index");
			}
		}

		public override int GetItemCount(out int count)
		{
			count = Configuration.ColorableItems.Count;
			return Microsoft.VisualStudio.VSConstants.S_OK;
		}
		#endregion

		#region MPF Accessor and Factory specialisation
		private LanguagePreferences preferences;
		public override LanguagePreferences GetLanguagePreferences()
		{
			if (this.preferences == null)
			{
				this.preferences = new LanguagePreferences(this.Site,
                                                        typeof(Wicresoft.VelocityLanguage.LanguageService.VelocityLanguageService).GUID,
														this.Name);
				this.preferences.Init();
			}

			return this.preferences;
		}

		public override Microsoft.VisualStudio.Package.Source CreateSource(IVsTextLines buffer)
		{
			return new Source(this, buffer, this.GetColorizer(buffer));
		}

		private IScanner scanner;
		public override IScanner GetScanner(IVsTextLines buffer)
		{
			if (scanner == null)
				this.scanner = new LineScanner();

			return this.scanner;
		}
		#endregion

		public override void OnIdle(bool periodic)
		{
			// from IronPythonLanguage sample
			// this appears to be necessary to get a parse request with ParseReason = Check?
			Source src = (Source) GetSource(this.LastActiveTextView);
			if (src != null && src.LastParseTime >= Int32.MaxValue >> 12)
			{
				src.LastParseTime = 0;
			}
			base.OnIdle(periodic);
		}
        

		public override Microsoft.VisualStudio.Package.AuthoringScope ParseSource(ParseRequest req)
		{
            Source source = (Source)this.GetSource(req.FileName);
            bool yyparseResult = false;

            // req.DirtySpan seems to be set even though no changes have occurred
            // source.IsDirty also behaves strangely
            // might be possible to use source.ChangeCount to sync instead
            AuthoringScope r = new AuthoringScope(source.ParseResult); 
            if (req.DirtySpan.iStartIndex != req.DirtySpan.iEndIndex
                || req.DirtySpan.iStartLine != req.DirtySpan.iEndLine)
            {
                Babel.Parser.ErrorHandler handler = new Babel.Parser.ErrorHandler();
                Babel.Lexer.Scanner scanner = new Babel.Lexer.Scanner(); // string interface
                Parser.Parser parser = new Parser.Parser();  // use noarg constructor

                parser.scanner = scanner;
                scanner.Handler = handler;
                parser.SetHandler(handler);
                scanner.SetSource(req.Text, 0);

                parser.MBWInit(req);
                yyparseResult = parser.Parse();

                // store the parse results
                // source.ParseResult = aast;
                source.ParseResult = null;
                //source.Braces = parser.Braces;

                //add controls to resolver
                Assembly a = Assembly.GetAssembly(typeof(IUmcDesignInterface));
                foreach (IUmcToolboxItem iumctbi in Controls)
                {
                    r.resolver.AddType(a.GetType(iumctbi.ComponentFullName));
                }

                
                //DealMembers(parser.Members, source, r);
                DealElement(parser.Elements, r);


                switch (req.Reason)
                {
                    case ParseReason.Check:
                    case ParseReason.HighlightBraces:
                    case ParseReason.MatchBraces:
                    case ParseReason.MemberSelect:

                    case ParseReason.MemberSelectAndHighlightBraces:
                        DealBraces(parser.Braces, source, req.Sink);
                        DealMarkup(parser.MarkupMatch, source, req);
                        break;
                    
                    case ParseReason.MethodTip:
                        


                        break;

                    default:
                        break;
                }

                // for the time being, just pull errors back from the error handler
                if (handler.ErrNum > 0)
                {
                    foreach (Babel.Parser.Error error in handler.SortedErrorList())
                    {
                        TextSpan span = new TextSpan();
                        span.iStartLine = span.iEndLine = error.line - 1;
                        span.iStartIndex = error.column;
                        span.iEndIndex = error.column + error.length;
                        req.Sink.AddError(req.FileName, error.message, span, Severity.Error);
                    }
                }
            }
            return r;
		}


        //braces contains () {} [] #if #end type
        private void DealBraces(IList<TextSpan[]> braces, Babel.Source source, AuthoringSink sink)
        {
            if (braces != null && source != null && sink != null)
            {
                foreach (TextSpan[] brace in braces)
                {
                    if (source.GetText(brace[1]).Equals("#end"))
                    {
                        TextSpan hideSpan = new TextSpan();
                        hideSpan.iStartIndex = brace[0].iStartIndex;
                        hideSpan.iStartLine = brace[0].iStartLine;
                        hideSpan.iEndIndex = brace[1].iEndIndex;
                        hideSpan.iEndLine = brace[1].iEndLine;
                        sink.ProcessHiddenRegions = true;
                        sink.AddHiddenRegion(hideSpan, source.GetText(brace[0]));
                    }
                    sink.MatchPair(brace[0], brace[1], 1);
                }
            }
        }

        //add members into resolver
        //private void DealMembers(IList<TextSpan[]> members, Babel.Source source,AuthoringScope aths)
        //{
        //    string temp;
        //    //add all members in the current context into resolver
        //    foreach (TextSpan[] ts in members)
        //    {
        //        temp = source.GetText(ts[0]);
        //        if (!aths.resolver.Members.Contains(temp))
        //        {
        //            aths.resolver.Members.Add(temp);
        //        }
        //    }
        //}

        //add markup
        private void DealMarkup(IList<TextSpan[]> markups, Babel.Source source, ParseRequest req)
        {
            if (markups != null && source != null && req.Sink != null)
            {
                foreach (TextSpan[] markup in markups)
                {
                    TextSpan hideSpan = new TextSpan();
                    hideSpan.iStartIndex = markup[0].iStartIndex;
                    hideSpan.iStartLine = markup[0].iStartLine;
                    hideSpan.iEndIndex = markup[1].iEndIndex;
                    hideSpan.iEndLine = markup[1].iEndLine;
                    req.Sink.ProcessHiddenRegions = true;
                    req.Sink.AddHiddenRegion(hideSpan, source.GetText(markup[0]));
                    req.Sink.MatchPair(markup[0], markup[1], 1);
                }
            }
        }

        private void DealElement(IList<ElementInfo> elements, AuthoringScope aths)
        {
            if (elements != null && aths != null)
            {
                aths.resolver.Elements = elements;
            }
        }

		public override string Name
		{
            get { return Babel.Configuration.Name; }
		}

        private IEnumerable<IUmcToolboxItem> controls = null;

        private IEnumerable<IUmcToolboxItem> Controls
        {
            get
            {
                if(controls == null)
                {
                    controls = ToolboxProvider.GetSupportedToolboxItem();
                }
                return controls;
            }
        }

	}
}